import { AssetManager, Node, NodePool, Prefab, error, instantiate, resources } from "cc";
import { GameEnum } from "./GameEnum";
import IGameScene from "./IGameScene";
import { BaseBullet } from "./bullet/BaseBullet";
import LevelPO from "./level/LevelPO";
import { BaseMonsterCharacter } from "./monster/BaseMonsterCharacter";

/*****************************
 *@file: GameSingle
 *@author: 陈吕唐
 *@desc: 游戏单例,战斗中资源管理
 *@date: 2024-03-12	10:37
 *****************************/
export default class GameSingle {
    /****************************************************************************************************************
     *Readonly 常量
     ****************************************************************************************************************/
    public static readonly single: GameSingle = new GameSingle();

    /****************************************************************************************************************
     *Property 定义变量
     ****************************************************************************************************************/
    /**
     * 当前游戏场景
     */
    public scene: IGameScene = undefined!;

    /******************************
     * 
     ******************************/
    /**
     * 当前游戏是否暂停中
     */
    public static stopGame: boolean = false;
    /******************************
     * 资源
     ******************************/
    /**
     * 子弹预制体缓存
     */
    private bulletPrefabMap: Map<number, Prefab> = new Map<number, Prefab>();

    /**
     * 子弹对象池缓存<子弹ID,对象池>
     */
    private bulletPoolMap: Map<number, NodePool> = new Map<number, NodePool>();

    /**
     * 怪物预制体缓存<怪物ID,预制体>
     */
    private monsterPrefabMap: Map<number, Prefab> = new Map<number, Prefab>();


    /**
     * 怪物节点池缓存<怪物ID,对应对象池>
     */
    private monsterPoolMap: Map<number, NodePool> = new Map<number, NodePool>();

    /**
     * 英雄ID预制体缓存<英雄ID,预制体>
     */
    private heroPrefabMap: Map<number, Prefab> = new Map<number, Prefab>();
    /***************************************************************
     *PropertyUI 引用的控件
     ***************************************************************/
    /****************************************************************************************************************
     * Lifecycle 生命周期相关方法
     ****************************************************************************************************************/
    /****************************************************************************************************************
     * Public 公有方法
     ****************************************************************************************************************/
    /**
     * 根据关卡数据，加载关卡所需资源
     * @param data 关卡数据
     * @param progress 进度
     */
    public loadAssets(data: LevelPO, progress: (finished: number, total: number, item: AssetManager.RequestItem) => void = undefined) {
        let monsterIds: number[] = data.waves.map((wave) => wave.monsterId);
        let promise = Promise.all([
            this.loadMonsterAssets(monsterIds, progress),
            this.loadBulletAssets(progress),
        ]);
        return promise;
    }

    /**
     * 获取子弹节点
     * @param id 子弹类型
     * @returns 
     */
    public getBullet(id: GameEnum.BulletType): Node {

        let pool: NodePool = this.bulletPoolMap.get(id);
        if (!pool) {
            pool = new NodePool(BaseBullet);
            this.bulletPoolMap.set(id, pool);
        }


        if (pool.size() <= 0) {
            let add = this.createBullet(id);
            pool.put(add);
        }

        let node: Node = pool.get(pool);
        return node;
    }

    /**
     * 创建子弹节点
     * @param id 子弹类型ID
     * @returns 
     */
    public createBullet(id: GameEnum.BulletType): Node {
        let prefab = this.bulletPrefabMap.get(id);
        if (!prefab) {
            error(`创建子弹节点失败,子弹预制体不存在或者未加载 ID:${id}`);
            return undefined!;
        }
        return instantiate(prefab);
    }

    /**
     * 根据怪物数据创建/获取怪物节点
     * @param value 怪物类型ID
     */
    public getMonster(monsterId: number): Node {
        /**
         * 怪物节点池
         */
        let pool: NodePool = this.monsterPoolMap.get(monsterId);
        if (!pool) {
            pool = new NodePool(BaseMonsterCharacter);
            this.monsterPoolMap.set(monsterId, pool);
        }

        if (pool.size() <= 0) {
            let add = this.createMonster(monsterId);
            pool.put(add);
        }

        let node = pool.get(pool);
        return node;
    }

    /**
     * 根据英雄ID获取英雄节点
     * @param heroId 英雄ID
     * @returns 
     */
    public getHero(heroId: number): Node {
        let prefab = this.heroPrefabMap.get(heroId);
        return instantiate(prefab);
    }

    /**
     * 创建怪物节点
     * @param monsterId 怪物类型ID
     * @returns 
     */
    public createMonster(monsterId: number): Node {
        let prefab = this.monsterPrefabMap.get(monsterId);
        if (!prefab) {
            error(`创建怪物节点失败,怪物预制体不存在或者未加载 ID:${monsterId}`);
            return undefined!;
        }
        return instantiate(prefab);
    };

    public loadMonsterAssets(ids: number[], progress: (finished: number, total: number, item: AssetManager.RequestItem) => void = undefined): Promise<Prefab[]> {
        let promise = Promise.all(ids.map(id => this.loadMonsterPrefab(id, progress)))
        return promise;
    }

    public loadHeroAssets(ids: number[], progress: (finished: number, total: number, item: AssetManager.RequestItem) => void = undefined): Promise<Prefab[]> {
        let promise = Promise.all(ids.map(id => this.loadHeroPrefab(id, progress)))
        return promise;
    }
    /****************************************************************************************************************
     * private 私有方法
     ****************************************************************************************************************/

    /**
     * 加载子弹预制体资源
     */
    private loadBulletAssets(progress: (finished: number, total: number, item: AssetManager.RequestItem) => void = undefined): Promise<Prefab[]> {
        let path = `bullet/`;

        let promise: Promise<Prefab[]> = new Promise((resolve, reject) => {
            resources.loadDir(path, progress, (err: Error, prefabs: Prefab[]) => {
                if (err) {
                    reject(err);
                    return;
                }

                prefabs.forEach((item) => {
                    this.bulletPrefabMap.set(parseInt(item.name), item);
                });
                resolve(prefabs);
            })
        });

        return promise;
    }

    /**
     * 根据怪物ID 加载怪物战斗预制体
     * @param id 怪物ID
     * @param progress 加载进度
     */
    private loadMonsterPrefab(id: number, progress: (finished: number, total: number, item: AssetManager.RequestItem) => void = undefined): Promise<Prefab> {
        if (this.heroPrefabMap.has(id)) {
            return Promise.resolve(this.heroPrefabMap.get(id));
        }

        let path = `monster/${id}`;
        let promise: Promise<Prefab> = new Promise((resolve, reject) => {
            resources.load(path, Prefab, progress, (err: Error, prefab: Prefab) => {
                if (err) {
                    reject(err);
                    return;
                }

                this.monsterPrefabMap.set(id, prefab);
                resolve(prefab);
            })
        });
        return promise;
    }


    /**
     * 根据英雄ID 加载英雄战斗预制体
     * @param id 英雄ID
     * @param progress 加载进度
     */
    private loadHeroPrefab(id: number, progress: (finished: number, total: number, item: AssetManager.RequestItem) => void = undefined): Promise<Prefab> {
        if (this.heroPrefabMap.has(id)) {
            return Promise.resolve(this.heroPrefabMap.get(id));
        }

        let path = `hero/${id}`;
        let promise: Promise<Prefab> = new Promise((resolve, reject) => {
            resources.load(path, Prefab, progress, (err: Error, prefab: Prefab) => {
                if (err) {
                    reject(err);
                    return;
                }

                this.heroPrefabMap.set(id, prefab);
                resolve(prefab);
            })
        });
        return promise;
    }
    /****************************************************************************************************************
     * Property Get && Set 属性的 get && set 函数
     ****************************************************************************************************************/
}